/** Copyright (c) 2012, peter.ducai@gmail.com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the Peter Ducai nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL PETER "daemonna" DUCAI BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.daemonna.fluentlee_core.cli;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;


/*
 * @author peter 'daemonna' ducai
 * @author peter.ducai@gmail.com 
 * @author http://code.google.com/p/fluentlee/
 */
public class executor {

    /**
     * detect OS, replace shell values and execute command
     *
     * @param params command to execute
     */
    public int executor(String[] params) throws IOException {

        //System.out.println("executor -----");
        int exitValue;
        String os = "";
        String[] cmd = new String[params.length + 2];
        if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {
            os = "windows";
            cmd[0] = "cmd";
            cmd[1] = "/C";
        } else if (System.getProperty("os.name").toLowerCase().indexOf("linux") > -1) {
            os = "linux";
            cmd[0] = "/bin/bash";
            cmd[1] = "-c";
        } else if (System.getProperty("os.name").toLowerCase().indexOf("unix") > -1) {
            os = "unix";
            cmd[0] = "/bin/sh";
            cmd[1] = "-c";
        } else if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) {
            os = "mac";
            cmd[0] = "/bin/bash";
            cmd[1] = "-c";
        } else if (System.getProperty("os.name").toLowerCase().indexOf("sunos") > -1) {
            //only solaris 10, 11 and higher
            os = "solaris";
            cmd[0] = "/bin/bash";
            cmd[1] = "-c";
        } else if (System.getProperty("os.name").toLowerCase().indexOf("z/os") > -1) {
            os = "z/os";
            cmd[0] = "/bin/sh";
            cmd[1] = "-c";
        } else {
            os = System.getProperty("os.name").toLowerCase().toString();
        }

        for (int ff = 0; ff < params.length; ff++) {
            if (!params[ff].isEmpty()) {
                cmd[ff + 2] = params[ff];
                System.out.println("ADDING: " + Integer.toString(ff + 2) + " - " + ff + " - " + params[ff]);
            }
        }

        //System.out.println("EXECUTOR: ");

        //I call processBuilder.redirectErrorStream(true); before processBuilder.start().
        //SO there should only be one combined output stream. –
        ProcessBuilder pb = new ProcessBuilder(cmd);
        pb.redirectErrorStream(true);
        Process p = pb.start();

        //The simplest way is to invoke the shell with the command line as the parameter.
        //After all, it's the shell which is interpreting "|" to mean "pipe the data between two processes".
        //Alternatively, you could launch each process separately, and read from the standard 
        //output of "ls -l", writing the data to the standard input of "grep" in your example.

        InputStream is = p.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line;
        //System.out.printf("Output of running %s is:\n",Arrays.toString(cmd));
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }

        //Wait to get exit value
        try {
            exitValue = p.waitFor();
            //System.out.println("\n\nExit Value is " + exitValue);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            exitValue = -1;
        }

        //System.out.println("EXECUTOR END: ");
        return exitValue;
    }
}


//Always read from the streams prior to calling waitFor. Otherwise you could end up waiting forever on Windows
// and other OS platforms whose I/O buffers can’t store enough from standard out and standard error to ensure 
//the program has finished. These platforms will pause the execution of whatever is running until something reads
// the buffered content from standard out and standard error. I would imagine all platforms suffer from this,
// but some platforms have larger buffers than others. Needless to say, always read from the streams first.
//
//Always read from standard error first. I ran across a bug where some OS platforms will always open 
//standard out, but never close it. What this means is that if you read from standard out first and the
// process only writes to standard error, you’ll hang forever waiting to read. If you read from standard
// error first, you’ll always be okay on these platforms because the OS seems to shutdown standard error.
// I think however, that the best way to handle all cases is to check both standard error and standard 
//out for readiness and only read from them if they have something to offer. The downside I could see
// here is that error isn’t ready, but eventually will be.